package laf.ui;

import jade.core.AID;
import jade.domain.introspection.GetValue;

import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JFrame;
import java.awt.Dimension;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DatasetChangeEvent;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import java.awt.GridBagLayout;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JList;
import javax.swing.JSlider;
import java.awt.GridBagConstraints;
import javax.swing.JCheckBox;
import javax.swing.SwingConstants;

import laf.agents.LanguageSymbolDictionary;
import laf.agents.LanguageSymbolGroundingHistory;
import laf.env.Attribute;
import laf.env.AttributeValue;
import laf.env.Enviroment;
import laf.lang.Symbol;

import javax.swing.JButton;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;

import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;

import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

public class CorrelationChart extends JFrame {

	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private ChartPanel chartPanel = null;
	private XYSeriesCollection seriesCollection = null;
	private JPanel jPanel = null;
	private JLabel jAttributesLabel = null;
	private JScrollPane jAttributesScrollPane = null;
	private JList jAttributesList = null;
	private JLabel jSymbolsLabel = null;
	private JScrollPane jSymbolsScrollPane = null;
	private JList jSymbolsList = null;
	private JSlider jTimeSlider = null;
	private JPanel jCenterPanel = null;
	private JPanel jTimePanel = null;
	private JPanel jTimeLabelPanel = null;
	private JLabel jTimeLabel = null;
	private JCheckBox jAutoUpdateCheckBox = null;
	private LanguageSymbolGroundingHistory groundingHistory; // @jve:decl-index=0:
	private JButton jUpdateButton = null;
	private JMenuBar jJMenuBar = null;
	private JMenu jFileMenu = null;
	private JMenuItem jSaveAsMenuItem = null;
	private JFileChooser jFileChooser = null;
	private JMenuItem jLoadMenuItem = null;

	/**
	 * This is the default constructor
	 */
	public CorrelationChart() {
		super();
		initialize();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(630, 282);
		this.setJMenuBar(getJJMenuBar());
		this.setContentPane(getJContentPane());
		this.setTitle("Attributes and Symbols Correlation");
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(new BorderLayout());
			jContentPane.add(getJPanel(), BorderLayout.WEST);
			jContentPane.add(getJCenterPanel(), BorderLayout.CENTER);
		}
		return jContentPane;
	}

	/**
	 * This method initializes chartPanel
	 * 
	 * @return org.jfree.chart.ChartPanel
	 */
	private ChartPanel getChartPanel() {
		if (chartPanel == null) {
			seriesCollection = new XYSeriesCollection();
			JFreeChart chart = ChartFactory.createXYLineChart(null, "Values", "Correlation", seriesCollection, PlotOrientation.VERTICAL, true, true, false);
			seriesCollection.removeChangeListener(chart.getXYPlot());
			chartPanel = new ChartPanel(chart);
		}
		return chartPanel;
	}

	/**
	 * This method initializes jPanel
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJPanel() {
		if (jPanel == null) {
			jSymbolsLabel = new JLabel();
			jSymbolsLabel.setText("Symbols");
			jAttributesLabel = new JLabel();
			jAttributesLabel.setText("Attributes");
			jPanel = new JPanel();
			jPanel.setLayout(new BoxLayout(getJPanel(), BoxLayout.Y_AXIS));
			jPanel.setPreferredSize(new Dimension(100, 100));
			jPanel.add(jAttributesLabel, null);
			jPanel.add(getJAttributesScrollPane(), null);
			jPanel.add(jSymbolsLabel, null);
			jPanel.add(getJSymbolsScrollPane(), null);
		}
		return jPanel;
	}

	/**
	 * This method initializes jAttributesScrollPane
	 * 
	 * @return javax.swing.JScrollPane
	 */
	private JScrollPane getJAttributesScrollPane() {
		if (jAttributesScrollPane == null) {
			jAttributesScrollPane = new JScrollPane();
			jAttributesScrollPane.setPreferredSize(new Dimension(100, 200));
			jAttributesScrollPane.setViewportView(getJAttributesList());
		}
		return jAttributesScrollPane;
	}

	/**
	 * This method initializes jAttributesList
	 * 
	 * @return javax.swing.JList
	 */
	private JList getJAttributesList() {
		if (jAttributesList == null) {
			jAttributesList = new JList();
			jAttributesList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
				public void valueChanged(javax.swing.event.ListSelectionEvent e) {
					drawChart();
				}
			});
		}
		return jAttributesList;
	}

	/**
	 * This method initializes jSymbolsScrollPane
	 * 
	 * @return javax.swing.JScrollPane
	 */
	private JScrollPane getJSymbolsScrollPane() {
		if (jSymbolsScrollPane == null) {
			jSymbolsScrollPane = new JScrollPane();
			jSymbolsScrollPane.setPreferredSize(new Dimension(100, 200));
			jSymbolsScrollPane.setViewportView(getJSymbolsList());
		}
		return jSymbolsScrollPane;
	}

	/**
	 * This method initializes jSymbolsList
	 * 
	 * @return javax.swing.JList
	 */
	private JList getJSymbolsList() {
		if (jSymbolsList == null) {
			jSymbolsList = new JList();
			jSymbolsList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
				public void valueChanged(javax.swing.event.ListSelectionEvent e) {
					drawChart();
				}
			});
		}
		return jSymbolsList;
	}

	/**
	 * This method initializes jTimeSlider
	 * 
	 * @return javax.swing.JSlider
	 */
	private JSlider getJTimeSlider() {
		if (jTimeSlider == null) {
			jTimeSlider = new JSlider();
			jTimeSlider.setToolTipText("Time");
			jTimeSlider.setMinorTickSpacing(1);
			jTimeSlider.setPaintTicks(true);
			jTimeSlider.setPaintLabels(true);
			jTimeSlider.setSnapToTicks(true);
			jTimeSlider.setInverted(false);
			jTimeSlider.setMajorTickSpacing(10);
			jTimeSlider.setMaximum(0);
			jTimeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
				public void stateChanged(javax.swing.event.ChangeEvent e) {
					updateData();
					drawChart();
					jTimeLabel.setText("Time: " + jTimeSlider.getValue() + " ");
				}
			});
		}
		return jTimeSlider;
	}

	/**
	 * This method initializes jCenterPanel
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJCenterPanel() {
		if (jCenterPanel == null) {
			jCenterPanel = new JPanel();
			jCenterPanel.setLayout(new BoxLayout(getJCenterPanel(), BoxLayout.Y_AXIS));
			jCenterPanel.setPreferredSize(new Dimension(200, 200));
			jCenterPanel.add(getChartPanel(), null);
			jCenterPanel.add(getJTimePanel(), null);
		}
		return jCenterPanel;
	}

	/**
	 * This method initializes jTimePanel
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJTimePanel() {
		if (jTimePanel == null) {
			jTimePanel = new JPanel();
			jTimePanel.setLayout(new BoxLayout(getJTimePanel(), BoxLayout.Y_AXIS));
			jTimePanel.add(getJTimeLabelPanel(), null);
			jTimePanel.add(getJTimeSlider(), null);
		}
		return jTimePanel;
	}

	/**
	 * This method initializes jTimeLabelPanel
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJTimeLabelPanel() {
		if (jTimeLabelPanel == null) {
			jTimeLabel = new JLabel();
			jTimeLabel.setText("Time: 0 ");
			jTimeLabel.setVerticalAlignment(SwingConstants.TOP);
			jTimeLabel.setHorizontalAlignment(SwingConstants.LEFT);
			jTimeLabel.setPreferredSize(new Dimension(60, 16));
			jTimeLabelPanel = new JPanel();
			jTimeLabelPanel.setLayout(new BoxLayout(getJTimeLabelPanel(), BoxLayout.X_AXIS));
			jTimeLabelPanel.setPreferredSize(new Dimension(200, 24));
			jTimeLabelPanel.add(jTimeLabel, null);
			jTimeLabelPanel.add(getJUpdateButton(), null);
			jTimeLabelPanel.add(getJAutoUpdateCheckBox(), null);
		}
		return jTimeLabelPanel;
	}

	/**
	 * This method initializes jAutoUpdateCheckBox
	 * 
	 * @return javax.swing.JCheckBox
	 */
	private JCheckBox getJAutoUpdateCheckBox() {
		if (jAutoUpdateCheckBox == null) {
			jAutoUpdateCheckBox = new JCheckBox();
			jAutoUpdateCheckBox.setText("Auto Update");
		}
		return jAutoUpdateCheckBox;
	}

	/**
	 * This method initializes jUpdateButton
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJUpdateButton() {
		if (jUpdateButton == null) {
			jUpdateButton = new JButton();
			jUpdateButton.setText("Update");
			jUpdateButton.setPreferredSize(new Dimension(74, 26));
			jUpdateButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					updateData();
				}
			});
		}
		return jUpdateButton;
	}

	public void updateData() {
		if (groundingHistory == null) {
			return;
		}

		int lastTick = groundingHistory.size() - 1;
		if (lastTick < 0) {
			return;
		}
		jTimeSlider.setMaximum(lastTick);
		int majTickSp = lastTick / 20 + 1;
		jTimeSlider.setMajorTickSpacing(majTickSp);
		jTimeSlider.setLabelTable(jTimeSlider.createStandardLabels(majTickSp));

		LanguageSymbolDictionary dict = groundingHistory.get(jTimeSlider.getValue());

		// TODO implement better symbol list update
		if (dict != null) {
			Object[] newSymbols = dict.getSymbols().toArray();
			int[] selection = jSymbolsList.getSelectedIndices();
			jSymbolsList.setListData(newSymbols);
			jSymbolsList.setSelectedIndices(selection);
		}
	}

	public void drawChart() {
		Object[] attrs = jAttributesList.getSelectedValues();
		if (attrs.length == 0) {
			return;
		}
		Object[] symbols = jSymbolsList.getSelectedValues();
		if (symbols.length == 0) {
			return;
		}
		int time = jTimeSlider.getValue();
		if (groundingHistory == null) {
			return;
		}
		LanguageSymbolDictionary dict = groundingHistory.get(time);
		if (dict == null) {
			return;
		}

		Symbol symbol;
		Attribute attr;
		XYSeries series;
		ArrayList<XYSeries> ss = new ArrayList<XYSeries>();
		DecimalFormat format = new DecimalFormat("#.##");
		for (Object objSymbol : symbols) {
			symbol = (Symbol) objSymbol;
			for (Object objAttr : attrs) {
				attr = (Attribute) objAttr;
				series = new XYSeries(symbol + " ~ " + attr + " (" + format.format(dict.getEntropy(symbol, attr)) + ")");
				for (AttributeValue attrValue : attr.getValues()) {
					series.add(attrValue.getValue(), dict.getCorrelation(symbol, attrValue));
					// dataset.addValue(dict.getCorrelation(symbol, attrValue),
					// symbol + " ~ " + attr, attrValue);
				}
				ss.add(series);
			}
		}

		seriesCollection.removeAllSeries();
		for (XYSeries series2 : ss) {
			seriesCollection.addSeries(series2);
		}
		
		chartPanel.getChart().getXYPlot().datasetChanged(new DatasetChangeEvent(seriesCollection, seriesCollection));

		// chartPanel.setChart(ChartFactory.createXYLineChart(null, "Values",
		// "Correlation", seriesCollection, PlotOrientation.VERTICAL, true,
		// true, false));

	}

	public void setGroundingHistory(LanguageSymbolGroundingHistory groundingHistory) {
		this.groundingHistory = groundingHistory;
		if (groundingHistory == null) {
			return;
		}
		groundingHistory.setListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (jAutoUpdateCheckBox.isSelected()) {
					updateData();
					jTimeSlider.setValue(jTimeSlider.getMaximum());
				}

			}
		});
		jAttributesList.setListData(groundingHistory.getAttributes().toArray());

		AID aid = groundingHistory.getAgent();
		if (aid != null) {
			setTitle(getTitle() + " [" + aid.getLocalName() + "]");
		}
		getJSaveAsMenuItem().setEnabled(true);
	}

	public LanguageSymbolGroundingHistory getGroundingHistory() {
		return groundingHistory;
	}

	/**
	 * This method initializes jJMenuBar
	 * 
	 * @return javax.swing.JMenuBar
	 */
	private JMenuBar getJJMenuBar() {
		if (jJMenuBar == null) {
			jJMenuBar = new JMenuBar();
			jJMenuBar.add(getJFileMenu());
		}
		return jJMenuBar;
	}

	/**
	 * This method initializes jFileMenu
	 * 
	 * @return javax.swing.JMenu
	 */
	private JMenu getJFileMenu() {
		if (jFileMenu == null) {
			jFileMenu = new JMenu();
			jFileMenu.setName("File");
			jFileMenu.setText("File");
			jFileMenu.add(getJSaveAsMenuItem());
			jFileMenu.add(getJLoadMenuItem());
		}
		return jFileMenu;
	}

	/**
	 * This method initializes jSaveAsMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getJSaveAsMenuItem() {
		if (jSaveAsMenuItem == null) {
			jSaveAsMenuItem = new JMenuItem();
			jSaveAsMenuItem.setText("Save As...");
			jSaveAsMenuItem.setEnabled(false);
			jSaveAsMenuItem.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					JFileChooser fc = getJFileChooser();
					int returnVal = fc.showSaveDialog(CorrelationChart.this);

					if (returnVal == JFileChooser.APPROVE_OPTION) {
						try {
							File saveFile = fc.getSelectedFile();

							// If there is no extension in the file name apply
							// default one
							if (saveFile.getName().indexOf('.') == -1) {
								saveFile = new File(saveFile.getAbsolutePath() + ".ghf");
							}
							FileOutputStream fos = new FileOutputStream(saveFile);
							ObjectOutputStream oos = new ObjectOutputStream(fos);
							oos.writeObject(groundingHistory);
							oos.close();
						} catch (IOException ioe) {
							// TODO Auto-generated catch block
							ioe.printStackTrace();
						}
					}
				}
			});
		}
		return jSaveAsMenuItem;
	}

	private JFileChooser getJFileChooser() {
		if (jFileChooser == null) {
			jFileChooser = new JFileChooser();
			jFileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Grounding History File (*.ghf)", "ghf"));
		}
		return jFileChooser;
	}

	/**
	 * This method initializes jLoadMenuItem
	 * 
	 * @return javax.swing.JMenuItem
	 */
	private JMenuItem getJLoadMenuItem() {
		if (jLoadMenuItem == null) {
			jLoadMenuItem = new JMenuItem();
			jLoadMenuItem.setText("Open...");
			jLoadMenuItem.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					JFileChooser fc = getJFileChooser();
					int returnVal = fc.showOpenDialog(CorrelationChart.this);

					if (returnVal == JFileChooser.APPROVE_OPTION) {
						File ghFile = fc.getSelectedFile();
						openFile(ghFile);
					}
				}
			});
		}
		return jLoadMenuItem;
	}

	public void openFile(File ghFile) {
		try {
			FileInputStream fis = new FileInputStream(ghFile);
			ObjectInputStream ois = new ObjectInputStream(fis);
			LanguageSymbolGroundingHistory groundingHistory = (LanguageSymbolGroundingHistory) ois.readObject();
			setGroundingHistory(groundingHistory);
			updateData();
			ois.close();
		} catch (IOException ioe) {
			// TODO Auto-generated catch block
			ioe.printStackTrace();
		} catch (ClassNotFoundException cnfe) {
			// TODO Auto-generated catch block
			cnfe.printStackTrace();
		}
	}

	public static void main(String[] args) {
		CorrelationChart frame = new CorrelationChart();
		frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		if (args.length > 0) {
			File ghFile = new File(args[0]);
			frame.openFile(ghFile);
		}
		frame.setVisible(true);
	}

} // @jve:decl-index=0:visual-constraint="36,10"
